/******************************************************************************
This file is part of AppKit.
Project: appkit
Author : FergusZeng
Email  : cblock@126.com
git	   : https://gitee.com/newgolo/appkit.git
*******************************************************************************
MIT License

Copyright (c) 2022 cblock@126.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#pragma once

#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include "appkit/basetype.h"
/**
 * @file packet.h
 * @brief 数据包组包解包
 */
namespace appkit {

/**
 * @class CRCCheck
 * @brief CRC校验
 */
class CRCCheck {
public:
    CRCCheck() {}
    ~CRCCheck() {}
    /**
     * @brief CRC16校验(多项式8005)
     * @param content 校验内容
     * @param initVal 初值(默认为0xffff)
     * @return uint16 校验值
     */
    static uint16 check16CRC8005(const std::string& content,
                                 const uint16& initVal = 0xffff);
};

struct MD5Context;
/**
 *  @class  MD5Check
 *  @brief  MD5校验类
 */
class MD5Check {
public:
    MD5Check();
    ~MD5Check();
    /**
     * @brief 获取文件的MD5值
     * @param fileName 文件名
     * @param md5sum 校验和
     * @return true
     * @return false
     */
    bool checkFile(const std::string& fileName, std::string* md5sum);
    /**
     * @brief 获取字符换的MD5值
     * @param srcString 字符串
     * @param md5sum 校验和
     * @return true
     * @return false
     */
    bool checkString(const std::string& srcString, std::string* md5sum);

private:
    void initContext(MD5Context* ctx);
    void calculate(MD5Context* ctx, uint8* data);
    void md5Write(MD5Context* hd, uint8* inbuf, int inlen);
    void md5Final(MD5Context* hd);
};

// 一种简单的二进制数据打包格式"包头(1B)+帧长(1B)+帧数据(nB)+CRC(2B)+包尾(1B)"
class AnyPacket {
    DECL_CLASSMETA(AnyPacket)
    class Frame {
        DECL_CLASSMETA(AnyPacket::Frame)
    public:
        /**
         * @brief 解包
         * @param packet 包数据
         * @return std::string 帧数据 
         */
        std::string decode(const std::string& packet);
        /**
         * @brief 封包
         * @param frame 帧数据 
         * @return std::string 包数据
         */
        std::string encode(const std::string& frame);
    };

public:
    std::vector<std::string> parse(const std::string& data);

private:
    std::string m_buffer;
};

/**
 * @class Base64
 * @brief BASE64编解码器
 */
class Base64 {
    DECL_CLASSMETA(Base64)

public:
    Base64() {}
    virtual ~Base64() {}
    /**
     * @brief base64编码
     * @param binDataIn 输入数据
     * @param b64DataOut 输出数据
     * @return true 编码成功
     * @return false 编码失败
     */
    bool encode(const std::string& binDataIn, std::string* b64DataOut);
    /**
     * @brief base64解码
     * @param b64DataIn 输入数据
     * @param binDataOut 输出数据
     * @return true 解码成功
     * @return false 解码失败
     */
    bool decode(const std::string& b64DataIn, std::string* binDataOut);
    /**
     * @brief base64编码(URL安全)
     * @param binDataIn 输入数据
     * @param b64DataOut 输出数据
     * @return true 编码成功
     * @return false 编码失败
     */
    bool encodeUrlSafe(const std::string& binDataIn, std::string* b64DataOut);
    /**
     * @brief base64解码(URL安全)
     * @param b64DataIn 输入数据
     * @param binDataOut 输出数据
     * @return true 解码成功
     * @return false 解码失败
     */
    bool decodeUrlSafe(const std::string& b64DataIn, std::string* binDataOut);

private:
    char findIndex(const char* str, char c);
};

/**
 * @class Parser
 * @brief 字符串解析
 */
class Parser {
    DECL_CLASSMETA(Parser)

public:
    /**
     * @brief 解析下一个字段
     * @param source 要解析的字符串
     * @param startPos 开始解析的位置
     * @param endflag 下一个结束标识
     * @param section_len 解析出来的字段长度(只有在返回值非NULL的情况下才有效)
     * @return char* 返回查找字段的首地址,如果没有查找到字段,则返回NULL
     * @note C风格的解析函数(一般用来解析固定分隔符的字符串,如AT指令)
     */
    char* parseNextSection(const char* source, uint32* startPos,
                           const char* endflag, uint32* section_len);
    /**
     * @brief 解析下一个字符串字段
     * @param source 要解析的字符串
     * @param startPos 开始解析的位置
     * @param endflag 下一个结束标识
     * @param result 解析出来的字符串
     * @return true
     * @return false
     * @note C风格的解析函数(一般用来解析固定分隔符的字符串,如AT指令)
     */
    bool parseNextString(const char* source, uint32* startPos,
                         const char* endflag, std::string* result);
    /**
     * @brief 解析下一个整数字段
     * @param source
     * @param startPos
     * @param endflag
     * @param result
     * @return true
     * @return false
     * @note C风格的解析函数(一般用来解析固定分隔符的字符串,如AT指令)
     */
    bool parseNextInt(const char* source, uint32* startPos, const char* endflag,
                      sint32* result);
};

/**
 * @brief 数据缓冲区
 * @class DataBuffer
 */
class DataBuffer {
    DECL_CLASSMETA(DataBuffer)

public:
    DataBuffer() {}
    virtual ~DataBuffer() {}
    /**
     * @brief 初始化
     * @param capacity 容量
     * @return true
     * @return false
     */
    virtual bool init(int capacity) = 0;
    /**
     * @brief 清空缓冲区
     */
    virtual void clear() = 0;
    /**
     * @brief 获取总容量
     * @return int
     */
    virtual int capacity() = 0;
    /**
     * @brief 获取剩余空间
     * @return int
     */
    virtual int space() = 0;
    /**
     * @brief 获取实际存储的数据长度
     * @return int
     */
    virtual int size() = 0;
    /**
     * @brief 放入数据
     * @param data
     * @param bytes
     * @return int
     */
    virtual int putData(char* data, int bytes) = 0;
    /**
     * @brief 取出数据
     * @param data
     * @param bytes
     * @return int
     */
    virtual int getData(char* data, int bytes) = 0;
};

/**
 * @class RingBuffer
 * @brief 循环缓冲
 */
class RingBuffer : public DataBuffer {
    DECL_CLASSMETA(RingBuffer)
public:
    RingBuffer();
    ~RingBuffer();
    bool init(int capacity) override;
    void clear() override;
    int capacity() override;
    int space() override;
    int size() override;
    int putData(char* data, int bytes) override;
    int getData(char* data, int bytes) override;

private:
    int m_startPos{0};
    int m_endPos{0};
    int m_occupant{0};
    int m_capacity{0};
    std::unique_ptr<char[]> m_bufPtr{nullptr};
    std::mutex m_mutex;
};

/**
 * @class LineBuffer
 * @brief 线性缓冲
 */
class LineBuffer : public DataBuffer {
public:
    LineBuffer();
    ~LineBuffer();
    bool init(int capacity) override;
    void clear() override;
    int capacity() override;
    int space() override;
    int size() override;
    int putData(char* data, int size) override;
    int getData(char* buf, int size) override;
    int find(char* str, int size);
    char operator[](int idx);

private:
    std::mutex m_mutex;
    char* m_buffer;
    char* m_endpos;
    char* m_curpos;
    int m_capacity{0};
};

}  // namespace appkit
